import { defineComponent, nextTick, onMounted, onUnmounted, ref, shallowRef } from 'vue'
import classes from './Controls.module.css'
import { moveable, draggable, resizeable, scaleable } from './controlsFn'
import {
  CaretBottom,
  CaretTop,
  CopyDocument,
  Delete,
  Document,
  DocumentAdd,
  DocumentCopy,
  Rank
} from '@element-plus/icons-vue'
import { ElIcon, ElText, ElTooltip } from 'element-plus'
import Guides from './Guides'
import { generateUUID, getVisibleRect, fromJson, toJson } from '../utils'
import { shortcutKey, getTip } from '../shortcut'

export default defineComponent(
  (props, { attrs, emit, expose }) => {
    const controlsRef = ref()
    const buttonListRef = ref()
    const moveRef = ref()
    const rightResizeRef = ref()
    const bottomResizeRef = ref()
    const cornerResizeRef = ref()
    const guidesRef = ref()
    const targetNode = shallowRef() // 控制的对象
    const buttonListHeight = 30
    // 操作按钮位置
    const buttonListPlacement = () => {
      const buttonListStyle = buttonListRef.value.style

      buttonListStyle.setProperty('top', `-${buttonListHeight}px`)
      buttonListStyle.setProperty('bottom', 'auto')
      buttonListStyle.setProperty('left', 'auto')
      buttonListStyle.setProperty('right', '0')

      const {
        top: visibleRectTop,
        bottom: visibleRectBottom,
        left: visibleRectLeft,
        right: visibleRectRight
      } = getVisibleRect(buttonListRef.value)

      const { bottom: controlsBottom } = controlsRef.value.getBoundingClientRect()

      const {
        top: buttonListTop,
        left: buttonListLeft,
        right: buttonListRight
      } = buttonListRef.value.getBoundingClientRect()

      if (buttonListTop < visibleRectTop) {
        if (controlsBottom + buttonListHeight > visibleRectBottom) {
          buttonListStyle.setProperty('top', '0')
          buttonListStyle.setProperty('bottom', `auto`)
        } else {
          buttonListStyle.setProperty('top', 'auto')
          buttonListStyle.setProperty('bottom', `-${buttonListHeight}px`)
        }
      }

      if (buttonListLeft < visibleRectLeft) {
        buttonListStyle.setProperty('left', '0')
        buttonListStyle.setProperty('right', 'auto')
      }

      if (buttonListRight > visibleRectRight) {
        buttonListStyle.setProperty('left', 'auto')
        buttonListStyle.setProperty('right', '0')
      }
    }

    const style = ref({
      ...(attrs.style as any)
    })
    const clearArr = []
    // 清除控制
    const clearControl = () => {
      targetNode.value = null
      controlsRef.value.remove()
      clearArr.forEach((clear) => clear())
    }

    // 定位到当前选择的node的元素
    const fit = (el: HTMLElement) => {
      if (!el) return

      el.append(controlsRef.value)

      nextTick(buttonListPlacement)
    }

    // vue不能watch dom，用回调从dom更新style
    const updateStyle = () => {
      if (!targetNode.value) return

      targetNode.value.updateStyle()
    }
    // move回调
    const moveCallback = (e) => {
      guidesRef.value?.showGuides(targetNode.value)
      updateStyle()
    }
    // 添加控制
    const control = (node) => {
      clearControl()
      guidesRef.value.hideGuides()

      targetNode.value = node

      fit(node.elRef.value)

      const clearRightResizeable = resizeable(
        node.elRef.value,
        rightResizeRef.value,
        0,
        updateStyle
      )
      const clearBottomResizeable = resizeable(
        node.elRef.value,
        bottomResizeRef.value,
        1,
        updateStyle
      )
      const clearCornerResizeable = resizeable(
        node.elRef.value,
        cornerResizeRef.value,
        2,
        updateStyle
      )
      // 绝对定位下的拖动，非drag
      const clearMoveable = moveable(node.elRef.value, moveRef.value, true, moveCallback)

      clearArr.push(
        clearMoveable,
        clearRightResizeable,
        clearBottomResizeable,
        clearCornerResizeable
      )

      // page可以缩放，非page可以拖放
      if (node.isNPage) clearArr.push(scaleable(node.elRef.value, updateStyle))
      else {
        // drag拖拽
        clearArr.push(draggable(node.elRef.value, moveRef.value))
      }
    }
    // 上移
    const moveBefore = (e: MouseEvent, node) => {
      e.preventDefault()
      e.stopPropagation()

      if (!node) return

      const parent = node.parent
      const index = parent.children.indexOf(node)
      const referenceNode = parent.children[index - 1]
      if (referenceNode) parent.insert(node, referenceNode)
    }
    // 下移
    const moveAfter = (e: MouseEvent, node) => {
      e.preventDefault()
      e.stopPropagation()

      if (!node) return

      const parent = node.parent
      const index = parent.children.indexOf(node)
      const referenceNode = parent.children[index + 1]
      if (referenceNode) parent.insert(node, referenceNode, 'after')
    }
    // 剪贴板
    const clipboard = ref('')
    // 复制节点
    const copyNode = (e: MouseEvent, node) => {
      // e.preventDefault() // 影响参数面板复制
      e.stopPropagation()

      if (!node) return

      const nodeJson = toJson(node)
      clipboard.value = nodeJson
    }
    // 克隆节点
    const cloneNode = (e: MouseEvent, node) => {
      e.preventDefault()
      e.stopPropagation()

      if (!node) return

      node.parent?.add(node.clone())
    }
    // 粘贴节点
    const pasteNode = (e: MouseEvent, node) => {
      e.preventDefault()
      e.stopPropagation()

      if (!node || !clipboard.value) return

      const newNode = fromJson(clipboard.value)

      newNode.traverse((n) => (n.uuid = generateUUID()))

      if (node?.isContainerNode) {
        if (node.isNPage && newNode.isNPage) {
          node.copy(newNode)
          ;[...newNode.children].forEach((child) => node.add(child))
        } else if (!newNode.isNPage) node.add(newNode)
      } else node.parent.add(newNode)
    }
    // 粘贴节点参数
    const pasteNodeParams = (e: MouseEvent, node) => {
      e.preventDefault()
      e.stopPropagation()

      if (!node || !clipboard.value) return

      const newNode = fromJson(clipboard.value)

      newNode.traverse((n) => (n.uuid = generateUUID()))

      node.copy(newNode)
    }
    // 删除节点
    const removeNode = (e: MouseEvent, node) => {
      e.preventDefault()
      e.stopPropagation()

      if (!node) return

      clearControl()

      const parent = node.parent
      parent?.remove(node)

      emit('removeNode')
    }
    // 快捷键
    const shortcutKeyEvent = (e) => {
      const { copyNode: copyN, pasteNode: pasteN, removeNode: removeN } = shortcutKey

      if (
        e.code == copyN.code &&
        e.shiftKey == copyN.shiftKey &&
        e.ctrlKey == copyN.ctrlKey &&
        e.altKey == copyN.altKey
      )
        copyNode(e, targetNode.value)

      if (
        e.code == pasteN.code &&
        e.shiftKey == pasteN.shiftKey &&
        e.ctrlKey == pasteN.ctrlKey &&
        e.altKey == pasteN.altKey
      )
        pasteNode(e, targetNode.value)

      if (
        e.code == removeN.code &&
        e.shiftKey == removeN.shiftKey &&
        e.ctrlKey == removeN.ctrlKey &&
        e.altKey == removeN.altKey
      )
        removeNode(e, targetNode.value)
    }

    onMounted(() => {
      document.addEventListener('keydown', shortcutKeyEvent)
    })

    onUnmounted(() => {
      document.removeEventListener('keydown', shortcutKeyEvent)
    })

    expose({
      control,
      clearControl,
      moveBefore,
      moveAfter,
      copyNode,
      cloneNode,
      pasteNode,
      pasteNodeParams,
      removeNode
    })

    const buttonColor = ref('#fff')

    return () => (
      <div ref={controlsRef} class={classes['controls']} style={style.value}>
        <div ref={rightResizeRef} class={classes['right-resize']}></div>
        <div ref={bottomResizeRef} class={classes['bottom-resize']}></div>
        <div ref={cornerResizeRef} class={classes['corner-resize']}></div>
        <div ref={buttonListRef} class={classes['button-list']}>
          <div class={classes['button']}>
            <ElText style={`--el-text-color: ${buttonColor.value};`}>
              {targetNode.value?.name}
            </ElText>
          </div>
          <div ref={moveRef} class={classes['button']}>
            <ElTooltip content="拖拽" effect="light" teleported={false} hide-after={0}>
              <ElIcon size={15} color={`${buttonColor.value}`}>
                <Rank></Rank>
              </ElIcon>
            </ElTooltip>
          </div>
          <div
            class={classes['button']}
            style={{ display: targetNode.value?.isNPage ? 'none' : 'initial' }}
            onPointerdown={(e) => moveBefore(e, targetNode.value)}
          >
            <ElTooltip content="上移" effect="light" teleported={false} hide-after={0}>
              <ElIcon size={15} color={`${buttonColor.value}`}>
                <CaretTop></CaretTop>
              </ElIcon>
            </ElTooltip>
          </div>
          <div
            class={classes['button']}
            style={{ display: targetNode.value?.isNPage ? 'none' : 'initial' }}
            onPointerdown={(e) => moveAfter(e, targetNode.value)}
          >
            <ElTooltip content="下移" effect="light" teleported={false} hide-after={0}>
              <ElIcon size={15} color={`${buttonColor.value}`}>
                <CaretBottom></CaretBottom>
              </ElIcon>
            </ElTooltip>
          </div>
          <div
            class={classes['button']}
            style={{ display: targetNode.value?.isNPage ? 'none' : 'initial' }}
            onPointerdown={(e) => cloneNode(e, targetNode.value)}
          >
            <ElTooltip content="克隆" effect="light" teleported={false} hide-after={0}>
              <ElIcon size={15} color={`${buttonColor.value}`}>
                <DocumentCopy></DocumentCopy>
              </ElIcon>
            </ElTooltip>
          </div>
          <div
            class={classes['button']}
            onPointerdown={(e) => pasteNodeParams(e, targetNode.value)}
          >
            <ElTooltip content="粘贴参数" effect="light" teleported={false} hide-after={0}>
              <ElIcon size={15} color={`${buttonColor.value}`}>
                <Document></Document>
              </ElIcon>
            </ElTooltip>
          </div>
          <div class={classes['button']} onPointerdown={(e) => copyNode(e, targetNode.value)}>
            <ElTooltip
              content={`复制 ${getTip(shortcutKey.copyNode)}`}
              effect="light"
              teleported={false}
              hide-after={0}
            >
              <ElIcon size={15} color={`${buttonColor.value}`}>
                <CopyDocument></CopyDocument>
              </ElIcon>
            </ElTooltip>
          </div>
          <div class={classes['button']} onPointerdown={(e) => pasteNode(e, targetNode.value)}>
            <ElTooltip
              content={`粘贴 ${getTip(shortcutKey.pasteNode)}`}
              effect="light"
              teleported={false}
              hide-after={0}
            >
              <ElIcon size={15} color={`${buttonColor.value}`}>
                <DocumentAdd></DocumentAdd>
              </ElIcon>
            </ElTooltip>
          </div>
          {/* ItemNode onMousedown stopPropagation 此处onClick会失效 */}
          <div
            class={classes['button']}
            style={{ display: targetNode.value?.isNPage ? 'none' : 'initial' }}
            onPointerdown={(e) => removeNode(e, targetNode.value)}
          >
            <ElTooltip
              content={`删除 ${getTip(shortcutKey.removeNode)}`}
              effect="light"
              teleported={false}
              hide-after={0}
            >
              <ElIcon size={15} color={`${buttonColor.value}`}>
                <Delete></Delete>
              </ElIcon>
            </ElTooltip>
          </div>
        </div>
        <Guides page={props.page} ref={guidesRef}></Guides>
      </div>
    )
  },
  {
    props: {
      page: {
        type: Object
      }
    },
    emits: ['removeNode']
  }
)
